<?php

namespace app\task\command;

use api\Xiaoju;
use app\admin\service\PlatformFinance;
use app\agent\service\AgentFinance;
use app\api\service\OilCardService;
use app\api\service\OilXiaojuService;
use app\api\service\PowerService;
use app\model\Agent;
use app\model\AgentFinanceDaily;
use app\model\BalanceWarningConfig;
use app\model\BalanceWarningLog;
use app\model\DmjPlatformUsers;
use app\model\FinanceDaily;
use app\model\MobileSupplier;
use app\model\OilAgentChannel;
use app\model\OilAgentConfig;
use app\model\OilCardOrder;
use app\model\OilOrderPull;
use app\model\PowerOrder;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
use think\facade\Db;

/**
 * 加油项目相关定时[没用].
 */
class Task extends Command
{
    public function __construct(Xiaoju $xiaoju)
    {
        parent::__construct();
        !defined('TP_START') && define('TP_START', microtime(true)); // 解决doLog 没有改变量问题
        $this->xiaoju = $xiaoju;
        set_time_limit(0);
        ignore_user_abort(true);
    }

    protected function configure()
    {
        $this->setName('task')
            ->addArgument('service', Argument::OPTIONAL, '参数1')
            ->addArgument('options', Argument::OPTIONAL, '参数2')
            ->setDescription('定时相关');
    }

    /**
     * cli 入口 根据service不同做不同处理.
     */
    protected function execute(Input $input, Output $output)
    {
        if ($input->getArgument('service')) {
            try {
                return $this->{$input->getArgument('service')}($input, $output);
            } catch (\Exception $e) {
                return $output->write(getExceptionMsg($e));
            }
        } else {
            return $output->write('未知service参数');
        }

        return $output->write('未知异常返回');
    }

    /**
     * 加油订单返佣处理.
     */
    protected function oilMontlyReCommiss(Input $input, Output $output)
    {
        // 每个月25号结算上个月
        if (25 != date('j')) {
            return $output->write('不是在25号');
        }

        $oilAgentConfig = OilAgentConfig::withTrashed()->select();
        if ($oilAgentConfig->isEmpty()) {
            return $output->write('没有oilAgentConfig 数据');
        }

        $dateTime = date('Y-m-d H:i:s');
        $lastMonthFrom = date('Y-m', strtotime('-1 month')).'-01 00:00:00';
        $lastMonthEnd = date('Y-m-d H:i:s', strtotime(date('Y-m-d').' 00:00:00') - 1);
        //$lastMonthEnd = $dateTime;

        Db::startTrans();
        try {
            // 锁定表
            $idArr = Db::name('oil_order')
                ->where('refund_status', 0)
                ->where('status', 1)
                ->whereBetween('pay_time', [$lastMonthFrom, $lastMonthEnd])
                ->lock(true)->field('id')->column('id');

            if (!$idArr) {
                Db::rollback();

                doWarning('没有未处理 加油佣金订单');

                return $output->write('结束，没有未处理 加油佣金订单,耗时：'.(microtime(true) - TP_START));
            }

            $res = Db::table('oil_order')
                ->alias('a')
                ->join('oil_agent_config b', 'a.agent_id=b.agent_id')
                ->join('agent c', 'a.agent_id=c.id')
                ->whereIn('a.id', $idArr)
                ->update([
                    'a.commissed_at' => $dateTime,
                    'a.commission' => Db::raw('if(c.oil_service=1,round(b.commission_rate/100*a.pay_amount),0)'),
                ]);
            if (!$res) {
                doWarning('加油佣金 更新订单行数为0');
            }

            // 入库到 资金明细
            $idStr = implode(',', $idArr);
            $sql = <<<sql
            INSERT INTO agent_balance_log (agent_id,type,amount,old_balance,new_balance,remark,quote_id,trade_no,created_at,updated_at) 
            SELECT
            ord.agent_id,
            9 AS type,
            sum(commission) AS amount,
            agent.balance AS old_balance,
            (sum(commission) + agent.balance) AS new_balance,
            '日期{$dateTime}，系统自动处理上个月加油分佣' AS remark,
            0 AS quote_id,
            '' AS trade_no,
            '{$dateTime}' as created_at,
            '{$dateTime}' as updated_at
        FROM
            agent
        INNER JOIN oil_order AS ord ON agent.id = ord.agent_id
        WHERE
            ord.commission > 0 and agent.oil_service=1
        AND ord.`status` = 1
        AND ord.refund_status = 0 and ord.id in({$idStr})
        GROUP BY
            agent.id           
sql;
            DB::query($sql);

            //更新到对应的代理商余额
            $subsql = Db::table('oil_order')
                    ->where('status', 1)
                    ->where('refund_status', 0)
                    ->whereIn('id', $idArr)
                    ->field('agent_id,sum(commission) commission')
                    ->group('agent_id')
                    ->buildSql();

            $res = Db::table('agent')
                    ->join($subsql.' oil_commiss_sum', 'agent.id=oil_commiss_sum.agent_id')
                    ->where('agent.id', '>', 0)->where('agent.oil_service', 1)
                    ->update(['agent.balance' => Db::raw('agent.balance+oil_commiss_sum.commission')]);

            if (!$res) {
                doWarning('加油佣金 更新代理商行数为0');
            }
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            doWarning('加油佣金 定时异常，异常信息：'.getExceptionMsg($e));
        }

        return $output->write('结束，耗时：'.(microtime(true) - TP_START));
    }

    /**
     * 每天了0点主动拉取最近3天订单（最大可以拉取7天），减少订单通知失败导致的遗漏.
     */
    public function dailyOrderCheck(Input $input, Output $output)
    {
        set_time_limit(0);
        ignore_user_abort(true);
        $days = $input->getArgument('options');
        if (!$days) {
            $days = 1;
        }
        //todo 主动拉取订单 和现有订单对比，状态不一致更新，不存在入库等
        try {
            $maxLoop = 1000; //一天按照10w订单的话，7天循环7000次 目前应该足够

            $oilXiaojuService = invoke(OilXiaojuService::class);

            $supplierInfo = MobileSupplier::where('code', OIL_SUPPLIER_ALIAS['xiaoju']['alias'])->find();
            $supplierId = $supplierInfo['id'] ?? 0;

            $dateTimeList = [];
            for ($day = 1; $day <= $days; ++$day) {
                if (1 === $day) {
                    $dateTimeList[] = ['start' => date('Y-m-d H:i:s', strtotime('-'.$day.' day')), 'end' => date('Y-m-d H:i:s')];
                } else {
                    $dateTimeList[] = ['start' => date('Y-m-d H:i:s', strtotime('-'.$day.' day')), 'end' => date('Y-m-d H:i:s', strtotime('-'.($day - 1).' day') - 1)];
                }
            }

            $insertBatch = [];
            foreach ($dateTimeList as $k => $v) {
                $param = ['queryStartTime' => $v['start'], 'queryEndTime' => $v['end'], 'pageSize' => 100, 'pageIndex' => 1];
                $loop = 0;
                while (true) {
                    $list = $oilXiaojuService->queryOrderList($param['pageIndex'], $param['pageSize'], $param['queryStartTime'], $param['queryEndTime']);
                    if (empty($list['data']['orderInfoList'])) { // 只关注data数据
                        break;
                    }

                    // 用户id
                    $platformToUser = []; // id => [user_id,app_id] 数组
                    $channelAgent = []; // channel_id => agent_id 数组
                    $outUserIdArr = array_column($list['data']['orderInfoList'], 'outUserId');
                    $platformUsers = DmjPlatformUsers::whereIn('id', $outUserIdArr)->field('id,user_id,app_id')->select();
                    if (!$platformUsers->isEmpty()) {
                        $platformUsers = $platformUsers->toArray();
                        foreach ($platformUsers as $k2 => $v2) {
                            $platformToUser[$v2['id']] = $v2;
                        }
                        // 代理商查询
                        $agentList = OilAgentChannel::whereIn('id', array_column($platformUsers, 'app_id'))->field('id,agent_id')->select();
                        if (!$agentList->isEmpty()) {
                            $agentList = $agentList->toArray();
                            foreach ($agentList as $k2 => $v2) {
                                $channelAgent[$v2['id']] = $v2['agent_id'];
                            }
                        }
                    }

                    // 从订单表里查询
                    $alrOrderArr = [];
                    $alrOrders = OilOrderPull::whereIn('out_order_id', array_column($list['data']['orderInfoList'], 'orderId'))
                                        ->field('out_order_id,user_id,cnid,agent_id,created_at')
                                        ->select();

                    if (!$alrOrders->isEmpty()) {
                        $alrOrders = $alrOrders->toArray();
                        foreach ($alrOrders as $k2 => $v2) {
                            $alrOrderArr[$v2['out_order_id']] = $v2;
                        }
                    }

                    foreach ($list['data']['orderInfoList'] as $k2 => $v2) {
                        $insertBatch[$v2['orderId']] = $oilXiaojuService->getOrderData($v2);

                        if (isset($alrOrderArr[$v2['orderId']])) {
                            $insertBatch[$v2['orderId']] = array_merge($insertBatch[$v2['orderId']], $alrOrderArr[$v2['orderId']]);
                        } else {
                            $insertBatch[$v2['orderId']]['supplier_id'] = $supplierId;
                            if (!empty($platformToUser[$v2['outUserId']]['user_id'])) {
                                $insertBatch[$v2['orderId']]['user_id'] = $platformToUser[$v2['outUserId']]['user_id'];
                                $insertBatch[$v2['orderId']]['cnid'] = $platformToUser[$v2['outUserId']]['app_id'];
                                $insertBatch[$v2['orderId']]['agent_id'] = $channelAgent[$platformToUser[$v2['outUserId']]['app_id']] ?? 0;
                            } else {
                                $insertBatch[$v2['orderId']]['user_id'] = 0;
                                $insertBatch[$v2['orderId']]['cnid'] = 0;
                                $insertBatch[$v2['orderId']]['agent_id'] = 0;
                            }
                        }
                    }

                    ++$param['pageIndex'];

                    ++$loop;
                    if ($loop >= $maxLoop) {
                        break;
                    }
                }
            }

            if ($insertBatch) {
                (new OilOrderPull())->replace()->saveAll(array_values($insertBatch));
            }
        } catch (\Exception $e) {
            doWarning(getExceptionMsg($e));
        }
        //invoke(OilXiaojuService::class)->queryOrderList();
    }

    /*
     目前 废弃。 保留用于生成旧的数据，操作的是insert 如果某天的数据已经存在，先删除再执行
    */
    public function dailyFinance(Input $input, Output $output)
    {
        $date = $input->getArgument('options');
        if (!$date) {
            $date = date('Y-m-d', strtotime('-1 day'));
        }
        $cacheKey = 'finance_'.$date;
        // 处理昨天的报表
        if (cache($cacheKey)) {
            return $output->write('已经执行过了');
        }

        $res = PlatformFinance::make()->insertDailyFinanceFromOrder($date, $date);
        if ($res['code']) {
            return $output->write('平台对账日报表生成失败，'.$res['message']);
        }
        $res = AgentFinance::make()->insertAgentDailyFinanceFromOrder($date, $date);
        if (!$res['code']) {
            cache($cacheKey, 1, 1800);
        } else {
            return $output->write('代理商对账日报表生成失败，'.$res['message']);
        }

        return $output->write('执行完毕');
    }

    /* 预生成代理商明天的日报表初始值 */
    public function premDailyFinance(Input $input, Output $output)
    {
        $date = $input->getArgument('options');
        if (!$date) {
            $date = date('Y-m-d', strtotime('+1 day'));
        }
        $output->write('预生成代理商明天日报初始数据，数量：'.(new AgentFinanceDaily())->initBatchDaily($date));
        if ((new FinanceDaily())->initDaily($date)) {
            $output->write(',预生成明天日报初始数据');
        }
    }

    /* 油卡订单批量上传后处理 */
    public function oilCardOrderUpload()
    {
        set_time_limit(0);
        halt((new OilCardService())->adminBatchPay());
    }

    /*
     定时处理油卡成功下单，但是没有推送或者推送失败
     处理的订单是 pay_status =1 refund_status=0 status = 0 后者status=2
     为了防止 多次推送或者程序的短暂时间差，加上pay_time为2分钟前的订单，一个月的订单
    */
    public function rePushOilCardOrder()
    {
        set_time_limit(0);
        if (cache('rePushOilCardOrder')) {
            return '进行中';
        }
        cache('rePushOilCardOrder', 1, 1800);
        $oilCardService = invoke(OilCardService::class);
        $where = [
            ['serv_type', '=', PRODUCT_OILCARD_SERV],
            ['pay_status', '=', 1],
            ['refund_status', '=', 0],
        ];
        $list = OilCardOrder::with(['oilCardOrder' => function ($query) {
            $query->field(['store_id', 'card_id', 'recharge_type', 'order_id']);
        }])->where($where)->whereBetween('pay_time', [date('Y-m-d H:i:s', strtotime('-1 months')), date('Y-m-d H:i:s', strtotime('-2 mins'))])->where(function ($query) {
            $query->where('status', 0)->whereOr('status', 2);
        })->field(['id', 'sn', 'origin_amount'])->select();

        $failMsg = '';

        if (!$list->isEmpty()) {
            try {
                $list = $list->toArray();
                foreach ($list as $k => $v) {
                    $param = [
                        'store_id' => $v['oilCardOrder']['store_id'],
                        'cardId' => $v['oilCardOrder']['card_id'],
                        'recharge_type' => $v['oilCardOrder']['recharge_type'],
                        'order_no' => $v['sn'],
                        'amount' => $v['origin_amount'],
                    ];
                    $res = $oilCardService->pushOrder($param, $v['id']);
                    if ($res['code']) {
                        $failMsg .= $v['id'].',';
                    }
                }
            } catch (\Exception $e) {
                doWarning(getExceptionMsg($e));
            }
        }

        if ($failMsg) {
            $failMsg = '，重新推送失败order_id:'.$failMsg;
        }

        cache('rePushOilCardOrder', null);

        echo '处理了条数据'.count($list).$failMsg;
    }

    /* 电费订单批量上传后处理 */
    public function powerOrderUpload()
    {
        set_time_limit(0);
        halt((new PowerService())->adminBatchPay());
    }

    /*
     定时处理电费成功下单，但是没有推送或者推送失败
     处理的订单是 pay_status =1 refund_status=0 status = 0 后者status=2
     为了防止 多次推送或者程序的短暂时间差，加上pay_time为2分钟前的订单，一个月的订单
    */
    public function rePushPowerOrder()
    {
        set_time_limit(0);
        if (cache('rePushPowerOrder')) {
            return '进行中';
        }
        cache('rePushPowerOrder', 1, 1800);
        $powerService = invoke(PowerService::class);
        $where = [
            ['serv_type', '=', PRODUCT_POWER_SERV],
            ['pay_status', '=', 1],
            ['refund_status', '=', 0],
        ];
        $list = PowerOrder::with(['powerOrder' => function ($query) {
            $query->field(['store_id', 'card_id', 'recharge_type', 'order_id']);
        }])->where($where)->whereBetween('pay_time', [date('Y-m-d H:i:s', strtotime('-1 months')), date('Y-m-d H:i:s', strtotime('-2 mins'))])->where(function ($query) {
            $query->where('status', 0)->whereOr('status', 2);
        })->field(['id', 'sn', 'origin_amount'])->select();

        $failMsg = '';

        if (!$list->isEmpty()) {
            try {
                $list = $list->toArray();
                foreach ($list as $k => $v) {
                    $param = [
                        'store_id' => $v['powerOrder']['store_id'],
                        'cardId' => $v['powerOrder']['card_id'],
                        'recharge_type' => $v['powerOrder']['recharge_type'],
                        'order_no' => $v['sn'],
                        'amount' => $v['origin_amount'],
                    ];
                    $res = $powerService->pushOrder($param, $v['id']);
                    if ($res['code']) {
                        $failMsg .= $v['id'].',';
                    }
                }
            } catch (\Exception $e) {
                doWarning(getExceptionMsg($e));
            }
        }

        if ($failMsg) {
            $failMsg = '，重新推送失败order_id:'.$failMsg;
        }

        cache('rePushPowerOrder', null);

        echo '处理了条数据'.count($list).$failMsg;
    }

    /*
     供应商余额不足提示
    */
    public function supplierBalanceWarning(Input $input, Output $output)
    {
        if (!isProd() && !$input->getArgument('options')) {
            return $output->write('非线上环境，除非强制才能执行');
        }
        $warningConfig = BalanceWarningConfig::where('state', 1)->where('type', 1)->find();
        $lockKey = 'task_lock_supplierBalanceWarning';
        $execTag = cache($lockKey);
        if ($execTag) {
            return $output->write('仍在处理中');
        }
        cache($lockKey, 1, 240); // 锁定4分钟
        $dateFrom = date('Y-m-d').' 00:00:00';
        $dateTime = date('Y-m-d H:i:s');
        $batchLog = [];
        try {
            // 话费供应商
            $mobileSupplierIdArr = BalanceWarningLog::where('type', 1)->where('datetime', '>=', $dateFrom)->column('relate_id');
            $query = Db::table('mobile_supplier')->where('is_deleted', 0)->where('balance', '<', $warningConfig['value']);
            if ($mobileSupplierIdArr) {
                $query = $query->whereNotIn('id', $mobileSupplierIdArr);
            }
            $list = $query->column('balance,name,id', 'id');
            if ($list) {
                foreach ($list as $k => $v) {
                    $res = sendSms('平台在'.$v['name'].'供应商的账户余额不足，请及时充值', $warningConfig['mobile']);
                    $batchLog[] = [
                        'relate_id' => $v['id'],
                        'balance' => $v['balance'],
                        'type' => 1,
                        'datetime' => $dateTime,
                        'state' => $res['code'] ? 0 : 1,
                        'msg' => $res['msg'],
                    ];
                }
            }

            // 其他供应商
            $baseSupplierIdArr = BalanceWarningLog::where('type', 2)->where('datetime', '>=', $dateFrom)->column('relate_id');
            $baseSupplierQuery = MobileSupplier::where('balance', '<', $warningConfig['value']);
            if ($baseSupplierIdArr) {
                $baseSupplierQuery = $baseSupplierQuery->whereNotIn('id', $baseSupplierIdArr);
            }
            $list = $baseSupplierQuery->column('name,balance,id', 'id');
            if ($list) {
                foreach ($list as $k => $v) {
                    $res = sendSms('平台在'.$v['name'].'供应商的账户余额不足，请及时充值', $warningConfig['mobile']);
                    $batchLog[] = [
                        'relate_id' => $v['id'],
                        'balance' => $v['balance'],
                        'type' => 2,
                        'datetime' => $dateTime,
                        'state' => $res['code'] ? 0 : 1,
                        'msg' => $res['msg'],
                    ];
                }
            }

            // 入口log
            if ($batchLog) {
                (new BalanceWarningLog())->saveAll($batchLog);
            }
        } catch (\Exception $e) {
            doLog(getExceptionMsg($e));
            cache($lockKey, null);
        }
        // 删除锁定key
        cache($lockKey, null);

        return $output->write('操作完成，耗时'.round((microtime(true) - TP_START), 2));
    }

    /*  代理商余额不足提示 */
    public function agentBalanceWarning(Input $input, Output $output)
    {
        if (!isProd() && !$input->getArgument('options')) {
            return $output->write('非线上环境，除非强制才能执行');
        }
        $warningConfig = BalanceWarningConfig::where('state', 1)->where('type', 2)->find();
        $lockKey = 'task_lock_agentBalanceWarning';
        $execTag = cache($lockKey);
        if ($execTag) {
            return $output->write('仍在处理中');
        }
        cache($lockKey, 1, 240); // 锁定4分钟
        $dateFrom = date('Y-m-d').' 00:00:00';
        $dateTime = date('Y-m-d H:i:s');
        $batchLog = [];
        try {
            $agentIdArr = BalanceWarningLog::where('type', 3)->where('datetime', '>=', $dateFrom)->column('relate_id');
            $agentQuery = Agent::where('balance', '<', $warningConfig->value);
            if ($agentIdArr) {
                $agentQuery = $agentQuery->whereNotIn('id', $agentIdArr);
            }
            $list = $agentQuery->column('company_name,balance,mobile,id', 'id');
            if ($list) {
                foreach ($list as $k => $v) {
                    $res = sendSms('【'.$v['company_name'].'】您的账户余额不足，请及时充值', $v['mobile']);
                    $batchLog[] = [
                        'relate_id' => $v['id'],
                        'balance' => $v['balance'],
                        'type' => 3,
                        'datetime' => $dateTime,
                        'state' => $res['code'] ? 0 : 1,
                        'msg' => $res['msg'],
                    ];
                }
            }
            // 入口log
            if ($batchLog) {
                (new BalanceWarningLog())->saveAll($batchLog);
            }
        } catch (\Exception $e) {
            doLog(getExceptionMsg($e));
            cache($lockKey, null);
        }
        // 删除锁定key
        cache($lockKey, null);

        return $output->write('操作完成，耗时'.round((microtime(true) - TP_START), 2));
    }

    /*
    每天处理 前两天的警报短信记录
    */
    public function deleteBalanceWarningLog()
    {
        BalanceWarningLog::where('datetime', date('Y-m-d', strtotime('-2')).' 00:00:00')->delete();
    }
}
